package se.slackers.floatingtableheader.client.generator;

import se.slackers.floatingtableheader.client.HeaderGenerator;
import se.slackers.floatingtableheader.client.browser.DOMUtil;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.Element;

public abstract class AbstractHeaderGenerator implements HeaderGenerator {
	protected static final String THEAD = "THEAD";
	protected static final String TBODY = "TBODY";
	protected static final String TR = "TR";
	protected static final String TD = "TD";
	protected static final String TH = "TH";
	
	protected static final DOMUtil util = GWT.create(DOMUtil.class);

	/**
	 * Returns true if tag is THEAD.
	 * 
	 * @param tag
	 * @return
	 */
	protected boolean isTHEAD(final String tag) {
		return THEAD.equalsIgnoreCase(tag);
	}

	/**
	 * Returns true if tag is TBODY.
	 * 
	 * @param tag
	 * @return
	 */
	protected boolean isTBODY(final String tag) {
		return TBODY.equalsIgnoreCase(tag);
	}

	/**
	 * Returns true if tag is TR.
	 * 
	 * @param tag
	 * @return
	 */
	protected boolean isTR(final String tag) {
		return TR.equalsIgnoreCase(tag);
	}

	/**
	 * Returns true if tag is either TD or TH.
	 * 
	 * @param tag
	 * @return
	 */
	protected boolean isCell(final String tag) {
		return TD.equalsIgnoreCase(tag) || TH.equalsIgnoreCase(tag);
	}

	/**
	 * Clones a node hierarchy and sets the size of all TD and TH elements.
	 * 
	 * @param source
	 * @return
	 */
	protected Element cloneWithSize(final Node source) {
		return cloneWithSize((Element) source);
	}

	/**
	 * Clones a node hierarchy and sets the size of all TD and TH elements.
	 * 
	 * @param source
	 * @return
	 */
	protected Element cloneWithSize(final Element source) {
		final String sourceName = source.getNodeName();
		final Element clonedRoot = (Element) source.cloneNode(false);
		if (source.getChildCount() == 0 || source.getNodeType() == Node.ELEMENT_NODE) {
			copySize(source, clonedRoot);
		}
		for (int i = 0; i < source.getChildCount(); i++) {
			final Node child = source.getChild(i);
			Node clone = null;
			if (child.getNodeType() != Node.ELEMENT_NODE) {
				clone = child.cloneNode(true);
			} else {
				final String name = child.getNodeName();
				if (isCell(name)) {
					clone = copySize(child, child.cloneNode(true));
				} else {
					clone = cloneWithSize((Element) child);
				}
			}
			if (isTR(sourceName)) {
				copyHeight(source, (Element) clone);
			}
			clonedRoot.appendChild(clone);
		}
		return clonedRoot;
	}

	/**
	 * Copies the size of <tt>source</tt> element to <tt>destination</tt>.
	 * 
	 * @param source
	 *            Source element.
	 * @param destination
	 *            Destination element.
	 * @return The destination element.
	 */
	protected Element copySize(final Node source, final Node destination) {
		return copySize((Element) source, (Element) destination);
	}

	/**
	 * Copies the size of <tt>source</tt> element to <tt>destination</tt>.
	 * 
	 * @param source
	 *            Source element.
	 * @param destination
	 *            Destination element.
	 * @return The destination element.
	 */
	protected Element copySize(final Element source, final Element destination) {
//		final float width = source.getClientWidth();
//		final float height = source.getClientHeight();
		final float width = util.getInnerWidth(source);
		final float height = util.getInnerHeight(source);

		final Style style = destination.getStyle();
		style.setWidth(width, Unit.PX);
		style.setHeight(height, Unit.PX);
		style.setProperty("minHeight", height, Unit.PX);
		style.setProperty("minWidth", width, Unit.PX);

		return destination;
	}

	protected Element copyHeight(final Element source, final Element destination) {
//		final float height = source.getClientHeight();
		final float height = util.getInnerHeight(source);
		
		final Style style = destination.getStyle();
		style.setHeight(height, Unit.PX);
		style.setProperty("minHeight", height, Unit.PX);
		return destination;
	}

	/**
	 * Returns true if the element contains a child with a certain class. Please note that this
	 * function only searches for string occurrences. So if an element has the class
	 * 'example-row-marker' if will return true if you search for 'example-row'.
	 * 
	 * @param element
	 * @param clazz
	 * @return
	 */
	protected boolean hasChildWithClass(final Element element, final String clazz) {
		try {
			for (int i = 0; i < element.getChildCount(); i++) {
				final Node child = element.getChild(i);
				if (child.getNodeType() == Node.ELEMENT_NODE) {
					if (((Element) child).getClassName().indexOf(clazz) >= 0) {
						return true;
					}
				}
			}
			return false;
		} catch (final Exception e) {
			return false;
		}
	}
}
